package com.fengpb.friday.conductor.core.execution;


import com.fengpb.friday.conductor.cache.LocalWorkflowDefCache;
import com.fengpb.friday.conductor.common.metadata.run.Workflow;
import com.fengpb.friday.conductor.common.metadata.run.Workflow.*;
import com.fengpb.friday.conductor.common.metadata.task.Task;
import com.fengpb.friday.conductor.common.metadata.workflow.WorkflowDef;
import com.fengpb.friday.conductor.core.WorkflowContext;
import com.fengpb.friday.conductor.core.execution.tasks.WorkflowSystemTask;
import com.fengpb.friday.conductor.core.utils.IDGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Component
public class WorkflowExecutor {

    private static final Logger log = LoggerFactory.getLogger(WorkflowExecutor.class);

    public static final String TRACE_KEY = "_traceId";

    private final DeciderService deciderService;

    private final LocalWorkflowDefCache workflowDefCache;

    public WorkflowExecutor(DeciderService deciderService, LocalWorkflowDefCache workflowDefCache) {
        this.deciderService = deciderService;
        this.workflowDefCache = workflowDefCache;
    }

    public Map<String, Object> startWorkflowWithOutput(String workflowName, int version, Map<String, Object> input) {
        WorkflowDef workflowDef = workflowDefCache.get(workflowName, version);
        return startWorkflowWithOutput(workflowDef, input, null, null);
    }

    public Map<String, Object> startWorkflowWithOutput(
        WorkflowDef workflowDefinition,
        Map<String, Object> workflowInput,
        String parentWorkflowId,
        String parentWorkflowTaskId) {

        String workflowId = IDGenerator.generate();

        Map<String, Object> output = new HashMap<>();
        output.put(TRACE_KEY, workflowId);

        Workflow workflow = new Workflow();
        workflow.setWorkflowId(workflowId);
        workflow.setWorkflowDefinition(workflowDefinition);
        workflow.setStatus(WorkflowStatus.RUNNING);
        workflow.setParentWorkflowId(parentWorkflowId);
        workflow.setParentWorkflowTaskId(parentWorkflowTaskId);
        workflow.setOwnerApp(WorkflowContext.get().getClientApp());
        workflow.setCreateTime(System.currentTimeMillis());
        workflow.setInput(workflowInput);
        workflow.setOutput(output);

        try {
            log.debug("start new workflow instance:{} with id:{}", workflow.getWorkflowName(), workflowId);
            decide(workflow);
        } catch (Exception e) {
            log.error("execute workflow instance:{}, version:{} error", workflow.getWorkflowName(), workflow.getWorkflowVersion(), e);
        }
        return workflow.getOutput();
    }

    /**
     * 开始决策并执行流程
     * @param workflow
     */
    public void decide(Workflow workflow) {
        try {
            DeciderService.DeciderOutcome outcome = deciderService.decide(workflow);
            if (outcome.isComplete) {
                completeWorkflow(workflow);
            }

            List<Task> tasksToBeScheduled = outcome.tasksToBeScheduled;
            workflow.getTasks().addAll(tasksToBeScheduled);

            if (scheduleTask(workflow, tasksToBeScheduled)) {
                decide(workflow);
            }
        } catch (TerminateWorkflowException e) {
            terminateWorkflow(workflow, e);
        } catch (Exception e) {
            log.error("decide workflow: {} error", workflow.getWorkflowId(), e);
            throw e;
        }
    }

    /**
     * 安排任务
     * @param workflow 流程实例
     * @param tasks 待安排的任务列表
     * @return
     */
    public boolean scheduleTask(Workflow workflow, List<Task> tasks) {
        boolean startedSystemTasks = false;
        if (tasks == null || tasks.isEmpty()) {
            return false;
        }

        for (Task task : tasks) {
            WorkflowSystemTask workflowSystemTask = WorkflowSystemTask.get(task.getTaskType());
            if (!workflowSystemTask.isAsync(task)) {
                task.setStartTime(System.currentTimeMillis());
                workflowSystemTask.execute(workflow, task, this);
                startedSystemTasks = true;
            } else {
                // TODO 异步任务
            }
        }

        return startedSystemTasks;
    }

    /**
     * 结束任务
     * @param workflow
     */
    public void completeWorkflow(Workflow workflow) {
        deciderService.updateWorkflowOutput(workflow, null);
        workflow.setStatus(WorkflowStatus.COMPLETED);
    }

    /**
     * 异常终止任务
     * @param workflow
     * @param e
     */
    public void terminateWorkflow(Workflow workflow, TerminateWorkflowException e) {

    }
}
